#pragma once

#include <iostream>
#include <vector>
#include "log.hpp"
#include "online.hpp"
#include "dboperator.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15
#define BLACK_CHESS 1
#define WHITE_CHESS 2   

typedef enum{
    GAME_START,
    GAME_OVER
}room_status;

class room{
private:
    uint64_t _room_id;
    uint64_t _player_count;
    uint64_t _white_id;
    uint64_t _black_id;
    room_status _stat;
    std::vector<std::vector<int>> _board;

public:
    room(uint64_t room_id)
        : _stat(GAME_START),_board(BOARD_ROW,std::vector<int>(BOARD_COL,0)),
        _room_id(room_id),_player_count(0)
    {   logmessage(NORMAL,"房间创建成功");  }
    ~room(){
        logmessage(NORMAL,"房间销毁成功");
    }
public:
    void add_white_user(uint64_t white_uid){
        _white_id = white_uid;
        _player_count++;
        logmessage(NORMAL,"白方棋子加入房间");
        return;
    }
    void add_black_user(uint64_t black_uid){
        _black_id = black_uid;
        _player_count++;
        logmessage(NORMAL,"黑方棋子加入房间");
        return;
    }
public:
    //沿着指定方向前后探索
    bool is_winn(int row,int col,int row_offset,int col_offset,int cur_color){
        int count = 1;
        int search_row = row+row_offset;
        int search_col = col+col_offset;
        while(search_row >=0 && search_row < BOARD_ROW &&
              search_col >= 0 && search_col < BOARD_COL &&
              _board[search_row][search_col] == cur_color){
            count++;
            //继续探索
            search_row +=row_offset;
            search_col +=col_offset;
        }
        search_row = row-row_offset;
        search_col = col-col_offset;
        while(search_row >=0 && search_row < BOARD_ROW &&
              search_col >= 0 && search_col < BOARD_COL &&
              _board[search_row][search_col] == cur_color){
            count++;
            search_row-=row_offset;
            search_col-=col_offset;
        }
        return count>=5;
    }
    uint64_t check_winner(int row,int col,int cur_color){
        if(is_winn(row,col,0,1,cur_color) || is_winn(row,col,1,0,cur_color)||
           is_winn(row,col,-1,-1,cur_color)|| is_winn(row,col,-1,1,cur_color)){
            return cur_color==WHITE_CHESS ? _white_id : _black_id;
        }
        return 0;
    }
    Json::Value handler_chess(Json::Value& req){
        Json::Value res = req;
        uint64_t uid = req["uid"].asUInt64();//谁下的棋
        int row = req["row"].asInt();
        int col = req["col"].asInt();
        //检测双方是在线,如果不在线就直接判负
        if(online_manager::get_online_manager()->is_in_gameroom(_white_id) == false){
            res["result"]= false;
            res["season"]="对方掉线，我方胜利";
            res["winner"]=(Json::UInt64)_black_id;
            return res;
        }
        if(online_manager::get_online_manager()->is_in_gameroom(_black_id) == false){
            res["result"]= false;
            res["season"]="对方掉线，我方胜利";
            res["winner"]=(Json::UInt64)_white_id;
            return res;
        }
        //判断走棋位置是否合理
        if(_board[row][col] != 0){
            res["result"]= false;
            res["season"]="该位置已经有棋子了";
            return res;
        }
        //获取当前的棋子是谁在走(颜色)
        int cur_color = uid == _black_id ? BLACK_CHESS : WHITE_CHESS;
        //棋子下注
        _board[row][col] = cur_color;
        //判断是否有胜利者
        uint64_t winner = check_winner(row,col,cur_color);
        if(winner != 0){
            res["reason"] = "五子连珠，胜利";
        }
        res["result"] = true;
        res["winner"] = (Json::UInt64)winner;
        //logmessage(DEBUG,"处理发送结果:%s",req.toStyledString().c_str());
        return res;
    }
    Json::Value handler_chat(Json::Value& req){
        std::string message= req["message"].asString();
        Json::Value res;
        res["optype"] = "chat";
        res["result"] = true;
        res["uid"] = req["uid"].asUInt64();
        res["message"] = message;
        return res;
    }
    void broadcast(Json::Value& res){
        //先判断双方在线
        //1.利用uid找到con
        std::string data;
        JsonUtil::serialization(data,res);

        server::connection_ptr white_con = online_manager::get_online_manager()->get_connectionptr_from_room(_white_id);
        server::connection_ptr black_con = online_manager::get_online_manager()->get_connectionptr_from_room(_black_id);
        if(white_con.get() != nullptr){
            white_con->send(data);
        }else{
            logmessage(NORMAL,"白方丢失链接");
        }

        if(black_con.get() != nullptr){
            black_con->send(data);
        }else{
            logmessage(NORMAL,"黑方丢失链接");
        }
        return;
    }
    void handler_request(Json::Value& req){
        //匹配房间号
        Json::Value res;
        uint64_t room_id = req["room_id"].asLargestUInt();
        if(room_id != _room_id){
            res["optype"] = req["optype"].asString();
            res["result"] = false;
            res["reason"] = "房间信息不匹配";
            return broadcast(res);
        }
        //处理不同的事物1.下棋 2.聊天
        if(req["optype"].asString() == "put_chess"){
            //处理下棋业务TODO
            res = handler_chess(req);
            //logmessage(NORMAL,"%s",res["reason"].asCString());
            if(res["winner"].asUInt64() != 0){
                //有胜利者产生,完成房间清理工作
                uint64_t winner_id = res["winner"].asUInt64();
                uint64_t loser_id = winner_id==_white_id? _black_id : _white_id;
                _stat = GAME_OVER;
                //TODO 调用数据库结算积分
                bool ret = Sql::getinstance()->win(winner_id);
                if(!ret)    logmessage(ERROR,"胜利者数据更新失败");
                ret = Sql::getinstance()->loser(loser_id);
                if(!ret)    logmessage(ERROR,"失败者数据更新失败");
                logmessage(DEBUG,"结算积分成功");
            }
            
        }else if(req["optype"].asString() == "chat"){
            //处理聊天业务TODO
            res = handler_chat(req);
        }else{
            //处理未被定义的业务
            res["optype"] = req["optype"].asString();
            res["result"] = false;
            res["reason"] = "未知类型的操作";
            
        }
        return broadcast(res);
    }
    uint64_t get_white_user(){
        return _white_id;
    }
    uint64_t get_black_user(){
        return _black_id;
    }
    void handler_exit(uint64_t uid){
        //如果是在gamestart时退出，直接判负，如果正常退出就正常
        Json::Value res;
        if(_stat == GAME_START){
            uint64_t winner_id = uid==_white_id ? _black_id:_white_id;
            res["optype"] = "put_chess";
            res["result"] = true;
            res["reason"] = "对方掉线，胜利";
            res["winner"] = (Json::UInt64)winner_id;
            res["room_id"] = (Json::UInt64)_room_id;
            res["uid"] = (Json::UInt64)uid;
            res["row"] = -1;
            res["col"] = -1;
            //结算
            uint64_t loser_id = winner_id == _white_id? _black_id:_white_id;
            Sql::getinstance()->win(winner_id);
            Sql::getinstance()->loser(loser_id);
            _stat = GAME_OVER;
            broadcast(res);
        }
        _player_count--;
        return;
    }
    int get_player_count(){
        return _player_count;
    }
    int get_room_id(){
        return _room_id;
    }
};

using room_ptr = std::shared_ptr<room>;

class room_manager{
private:
    static room_manager* _instance;
    static std::mutex _mutex;
    uint64_t _next_room_id;
    std::unordered_map<uint64_t,uint64_t> _user_to_roomid; //存储用户Id和房间id的映射关系
    std::unordered_map<uint64_t,room_ptr> _roomid_to_room; //存储房间id到房间指针的映射
public:
    room_manager() :_next_room_id(1) {
        logmessage(NORMAL,"房间管理器初始化成功");
    }
    ~room_manager(){
        logmessage(NORMAL,"房间管理器销毁成功");
    }
    static room_manager* get_instance(){
        if(_instance == nullptr){
            std::unique_lock<std::mutex> lock(_mutex);
            if(_instance == nullptr){
                _instance = new room_manager();
            }
        }
        return _instance;
    }
public:
    room_ptr create_room(uint64_t uid1,uint64_t uid2){
        //验证双方用户是否在线
        if(online_manager::get_online_manager()->is_in_gameroom(uid1)){
            logmessage(NORMAL,"用户1不在线");
            return room_ptr();
        }        
        if(online_manager::get_online_manager()->is_in_gameroom(uid2)){
            logmessage(NORMAL,"用户2不在线");
            return room_ptr();
        }
        room_ptr cur(new room(_next_room_id));
        cur->add_white_user(uid1);
        cur->add_black_user(uid2);
        //将信息存储起来
        std::unique_lock<std::mutex> lock(_mutex);
        _user_to_roomid.insert(std::make_pair(uid1,_next_room_id));
        _user_to_roomid.insert(std::make_pair(uid2,_next_room_id));
        _roomid_to_room.insert(std::make_pair(_next_room_id,cur));
        _next_room_id++;
        return cur;
    }
    room_ptr get_room_by_uid(uint64_t uid){
        std::unique_lock<std::mutex> lock(_mutex);
        auto id = _user_to_roomid.find(uid);
        if(id == _user_to_roomid.end()){
            return room_ptr();
        }
        auto it = _roomid_to_room.find(id->second);
        if(it == _roomid_to_room.end()){
            return room_ptr();
        }
        return it->second;
    }
    room_ptr get_room_by_rid(uint64_t roomid){

        auto it = _roomid_to_room.find(roomid);
        if(it == _roomid_to_room.end()){
            return room_ptr();
        }
        return it->second;
    }
    void remove_room(uint64_t roomid){
        room_ptr rp = get_room_by_rid(roomid);
        if(rp.get() == nullptr){
            logmessage(ERROR,"销毁房间失败，roomid错误");
            return;
        }
        //通过房间获取用户
        uint64_t uid1 = rp->get_black_user();
        uint64_t uid2 = rp->get_white_user();
        std::unique_lock<std::mutex> lock(_mutex);
        _roomid_to_room.erase(roomid);
        _user_to_roomid.erase(uid1);
        _user_to_roomid.erase(uid2);
        return;
    }
    void remove_user(uint64_t uid){
        room_ptr rp = get_room_by_uid(uid);
        if(rp.get() == nullptr) return;
        rp->handler_exit(uid);
        //当玩家人数为空时就删除房间
        if(rp->get_player_count() == 0){
            remove_room(rp->get_room_id());
        }
        return;
    }
};

room_manager* room_manager::_instance = nullptr;
std::mutex room_manager::_mutex;